package cn.col.gszf.service.impl;

import cn.col.gszf.mapper.ChannelMapper;
import cn.col.gszf.mapper.VoteOptionsMapper;
import cn.col.gszf.mapper.VotesMapper;
import cn.col.gszf.pojo.Channel;
import cn.col.gszf.pojo.UserVotes;
import cn.col.gszf.pojo.Vote;
import cn.col.gszf.pojo.VoteOptions;
import cn.col.gszf.service.ChannelService;
import cn.col.gszf.service.RedisService;
import cn.col.gszf.service.UserVotesService;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author wanghong
 * @created 2024-06-18 23:46
 * @description 频道接口实现类
 **/
@Service
public class ChannelServiceImpl implements ChannelService {


    @Resource(name = "redis-service")
    private RedisService redisService;

    @Resource(name = "userVotesServiceImpl")
    private UserVotesService userVotesService;

    @Resource
    private ChannelMapper channelMapper;

    @Resource
    private VotesMapper votesMapper;

    @Resource
    private VoteOptionsMapper voteOptionsMapper;

    @Override
    public Channel getById(Integer id) {
        Channel channel = channelMapper.selectById(id);
        List<Vote> list = votesMapper.selectList(new QueryWrapper<Vote>().eq("channel_id", id));
        if (ObjectUtil.isEmpty(list)) {
            return channel;
        }
        List<Integer> voteList = new ArrayList<>();
        list.forEach(vote -> {
            voteList.add(vote.getId());
        });
        List<VoteOptions> voteOptionsList = voteOptionsMapper.selectList(new QueryWrapper<VoteOptions>().in("vote_id", voteList));
        list.forEach(vote -> {
            voteOptionsList.forEach(voteOptions -> {
                if (vote.getId().equals(voteOptions.getVoteId())) {
                    vote.setVoteOptionsList(voteOptionsList);
                }
            });
        });
        channel.setVotes(list);
        return channel;
    }

    @Override
    public List<Channel> findAllToChannelInfo(List<Integer> list) {
        QueryWrapper<Channel> q = new QueryWrapper<>();
        q.in("id", list);
        return channelMapper.selectList(q);
    }

    @Override
    public List<Channel> findAllToChannelInfo() {
        return channelMapper.selectList(null);
    }

    @Override
    @Transactional
    public Boolean deleteById(Integer id) {
        Channel byId = channelMapper.selectById(id);

        redisService.zRemove(RedisService.REDIS_HOT_LIST, id);
        for (Vote vote : byId.getVotes()) {
            int[] voteOptionsId = vote.getVoteOptionsList().stream().mapToInt(VoteOptions::getId).toArray();
            ArrayList<String> keys = new ArrayList<>();
            for (Integer idx : voteOptionsId) keys.add("options_" + idx);
            redisService.remove(keys.toArray(new String[0]));
        }
        channelMapper.deleteById(id);
        return Boolean.TRUE;
    }

    @Transactional
    @Override
    public Channel save(Channel channel) {
        channel.setVotingNum(-1);
        Channel newChannel;
        int isInsert = channelMapper.insert(channel);
        if (isInsert > 0) {
            newChannel = channel;
        } else {
            newChannel = null;
        }

        redisService.zAdd(RedisService.REDIS_HOT_LIST, channel.getId().toString(), 0);

        if (newChannel != null) {
            for (Vote vote : newChannel.getVotes()) {
                int[] voteOptionsIds =
                        vote.getVoteOptionsList().stream().mapToInt(VoteOptions::getId).toArray();
                ArrayList<String> keys = new ArrayList<>();
                for (Integer id : voteOptionsIds) keys.add("options_" + id);
                redisService.add(keys.toArray(new String[0]));
            }
        }
        return newChannel;
    }

    @Override
    public Channel writeBack(Integer channelId) {
        Channel old = channelMapper.selectById(channelId);
        Integer peopleNum = (Integer) redisService.get("channel_" + old.getId());
        redisService.remove("channel_" + old.getId());
        old.setVotingNum(peopleNum);
        for (int i = 0; i < old.getVotes().size(); i++) {
            Vote vote = old.getVotes().get(i);
            for (int j = 0; j < vote.getVoteOptionsList().size(); j++) {
                VoteOptions voteOptions = vote.getVoteOptionsList().get(j);
                int singeNum = (int) redisService.get("option_" + voteOptions.getId());
                voteOptions.setTotalVoting(singeNum);
                redisService.remove("option_" + voteOptions.getId());
            }
        }
        channelMapper.insert(old);
        return old;
    }

    @Override
    public Channel update(Channel channel) {
        Channel newChannel;
        int isInsert = channelMapper.updateById(channel);
        if (isInsert > 0) {
            newChannel = channel;
        } else {
            newChannel = null;
        }
        if (newChannel != null) {
            List<Vote> voteList = votesMapper.selectList(new QueryWrapper<Vote>().eq("channel_id", channel.getId()));
            List<Integer> list = new ArrayList<>();
            voteList.forEach(vote -> {list.add(vote.getId());});
            List<VoteOptions> voteOptionsList = voteOptionsMapper.selectList(new QueryWrapper<VoteOptions>().in("vote_id", list));
            voteList.forEach(vote -> {
                vote.setVoteOptionsList(voteOptionsList);
            });
            newChannel.setVotes(voteList);
            for (Vote vote : newChannel.getVotes()) {
                for (VoteOptions voteOptions : vote.getVoteOptionsList()) {
                    if (!redisService.exists("option_" + voteOptions.getId())) {
                        redisService.add("option_" + voteOptions.getId());
                    }
                }
            }
        }
        return newChannel;
    }

    /**
     * @Description 查询所有频道信息
     * @Param:
     * @return: List<Channel>
     * @Author: wanghong
     * @Date: 2024/6/23
     */
    @Override
    public List<Channel> selectAllChannelInfo() {
        QueryWrapper<Channel> queryWrapper = new QueryWrapper<>();
        List<Channel> channelList = channelMapper.selectList(queryWrapper);
        //获取zset集合中频道对应的score值
        channelList.forEach(channel -> {
            if (redisService.exists(RedisService.REDIS_HOT_LIST)) {
                channel.setVotingNum(
                        redisService.zGetScore(RedisService.REDIS_HOT_LIST, String.valueOf(channel.getId()))
                );
            } else {
                channel.setVotingNum(0);
            }
        });
        return channelList;
    }

    /**
     * @Description 根据id查询频道信息
     * @Param: channelId，VID，model，ip
     * @return: Channel
     * @Author: wanghong
     * @Date: 2024/6/23
     */
    @Override
    public Channel getById(Integer channelId, String VID, Model model, String ip, HttpServletResponse response) {
        try {
            //利用Guava进行对象判空
            Optional<Channel> channelOptional = Optional.ofNullable(channelMapper.selectById(channelId));
            Channel channel = channelOptional.orElse(null);
            boolean canVote = true;
            //如果不存在VID那么就创建，存在就会查找是否可以填写
            if (VID == null) {
                //生成校验码，只有客户端拿到并和服务端redis数据库进行比对那才能投票
                VID = IdUtil.simpleUUID();
                Cookie cookie = new Cookie("VID", VID);
                cookie.setPath("/");
                cookie.setMaxAge(3600 * 24 * 365 * 3);
                response.addCookie(cookie);
            } else {
                canVote = userVotesService.checkUser(ip, VID, channelId);
            }

            //向客户端设置一个用于判别投票的optionId;
            Cookie optionCookie = new Cookie("optionId", IdUtil.simpleUUID());
            optionCookie.setPath("/vote");
            response.addCookie(optionCookie);

            if (canVote) {
                //如果可投票，设置一个20分钟的key
                redisService.setString(VID, optionCookie.getValue(), 20 * 60);
                model.addAttribute("canVote", 0);
            } else {
                List<UserVotes> records = userVotesService.getByVIDAndChannelId(VID, channelId); //获取投票信息
                if (records.isEmpty()) {
                    model.addAttribute("canVote", 1); //不可投票，IP数量超限}
                } else {
                    model.addAttribute("canVote", 2); //不可投票，已经填写过投票数据
                    model.addAttribute("answers", JSONUtil.toJsonStr(records.get(0).getVotes()));
                }
            }

            //处理投票实体
            Wrapper<Vote> wrapper = new QueryWrapper<Vote>().eq("channel_id",channelId);
            channel.setVotes(votesMapper.selectList(wrapper));
            return channel;
        } catch (Exception e) {
            e.printStackTrace();
            return new Channel();
        }
    }
}
